package com.common.model.config;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.common.model.pojo.Keyword;
import com.common.model.pojo.Order;
import com.common.model.pojo.Query;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.annotation.ModelFactory;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.util.*;

/**
 * 描述：
 * 查询参数处理
 * @author xianggj
 * @Date 2021/11/26 17:43
 **/
public class QueryArgumentResolvers implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
       Class<?> c = methodParameter.getParameterType();
       return c == Query.class;
    }

    /**
     * 前端传递参数不规则
     *
     * @param methodParameter
     * @param modelAndViewContainer
     * @param nativeWebRequest
     * @param webDataBinderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest, 
                                  WebDataBinderFactory webDataBinderFactory) throws Exception {
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        Query query = new Query();
        Enumeration<String> parameterNames = request.getParameterNames();
        if (!parameterNames.hasMoreElements()){
            return query;
        }
        Field[] fields = ReflectUtil.getFields(Query.class);
        List<String> fieldNams = new ArrayList<>();
        for (Field f : fields) {
            String name = f.getName();
            fieldNams.add(name);
        }
        //Map 特殊处理
        Map<String, String> params = new LinkedHashMap<>();
        query.setParams(params);
        while(parameterNames.hasMoreElements()) {
            String parameterName = parameterNames.nextElement();
            String val = request.getParameter(parameterName);
            parameterName = URLDecoder.decode(parameterName, CharsetUtil.UTF_8);
            String paraName = StrUtil.subBefore(parameterName, "[", false);
            //属性传递 取最后一个值作为key
            if (paraName.contains(".")){
                String key = StrUtil.subAfter(parameterName, ".", true);
                params.put(key, val);
                continue;
                //不以元数据开头的 都当成params参数
            } else if (!fieldNams.contains(paraName)) {
                params.put(parameterName, val);
            }
        }
        String name = ModelFactory.getNameForParameter(methodParameter);
        ExtendedServletRequestDataBinder webRequestDataBinder =
                (ExtendedServletRequestDataBinder)webDataBinderFactory.createBinder(nativeWebRequest, query, name);
        webRequestDataBinder.bind(request);
        return query;
    }

    /**
     * 手写解析方法
     * 废弃
     * @param request
     * @return
     */
    @Deprecated
    private Object analysisParamsToQuery(HttpServletRequest request) {
        TimeInterval timer = DateUtil.timer();
        Query query = new Query();
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (MapUtil.isEmpty(parameterMap)){
            return query;
        }
        //真没找到好办法
        Map<String, String> params = new LinkedHashMap<>();
        List<Order> orders = new ArrayList<>();
        List<Keyword> keywords = new ArrayList<>();
        query.setParams(params);
        query.setOrders(orders);
        query.setKeywords(keywords);
        String[][] ordersArray = new String[10][2];
        String[][] keywordsArray = new String[10][2];
        Field[] fields = ReflectUtil.getFields(Query.class);
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String key = entry.getKey();
            String[] value = entry.getValue();
            if (value == null || key == null){
                continue;
            }
            String val = value[0];
            if (fields[0].getName().equalsIgnoreCase(key)){
                query.setCurrent(Integer.valueOf(val));
                continue;
            }
            if (fields[1].getName().equalsIgnoreCase(key)){
                query.setSize(Integer.valueOf(val));
                continue;
            }
            if (key.startsWith("params.")){
                params.put(key.substring(7), val);
                continue;
            }
            if (key.startsWith("params[")){
                key = StrUtil.removeAll(key, '\'', '"');
                params.put(key.substring(7, key.length()-1), val);
                continue;
            }
            if (key.contains("[")){
                int num = 0;
                String attrStr = null;
                if (key.contains(".")){
                    String index = StrUtil.subBetween(key, "[", "]");
                    if (!StringUtils.isNumeric(index)){
                        continue;
                    }
                    num = Integer.parseInt(index);
                    attrStr = StrUtil.subAfter(key, ".", true);
                } else {
                    String[] keys = StrUtil.subBetweenAll(key, "[", "]");
                    if (!StringUtils.isNumeric(keys[0])){
                        continue;
                    }
                    num = Integer.parseInt(keys[0]);
                    attrStr = StrUtil.removeAll(keys[1], '\'', '"');
                }
                switch (attrStr){
                    case "orderField":
                        ordersArray[num][0] = val;
                        break;
                    case "asc":
                        ordersArray[num][1] = val;
                        break;
                    case "keyword":
                        keywordsArray[num][1] = val;
                        break;
                    case "keywordField":
                        keywordsArray[num][0] = val;
                        break;
                    default:
                }
            }
        }
        for (String[] orderArray: ordersArray) {
            if (orderArray == null || orderArray.length != 2 || orderArray[0] == null){
                continue;
            }
            orders.add(new Order(orderArray[0], Boolean.valueOf(orderArray[1])));
        }
        for (String[] keywordArray: keywordsArray) {
            if (keywordArray == null || keywordArray.length != 2 || keywordArray[0] == null){
                continue;
            }
            keywords.add(new Keyword(keywordArray[1], keywordArray[0]));
        }
        System.out.println(timer.interval());
        return query;
    }
}
